#include <sys/timerfd.h>
#include <sys/eventfd.h>
#include <sys/syscall.h>
#include <cstring>
#include <csignal>
#include "EventLoop.h"
#include "Epoll.h"
#include "Channel.h"
#include "Connection.h"

int EventLoop::CreateTimerFd(int sec)
{
    int fd = ::timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
    struct itimerspec timeout{};
    memset(&timeout, 0, sizeof(struct itimerspec));
    timeout.it_value.tv_sec = sec;
    timeout.it_value.tv_nsec = 0;
    timerfd_settime(fd, 0, &timeout, nullptr);
    return fd;
}

EventLoop::EventLoop(bool flag, int timetvl, int timeout)
        : ep(new Epoll), mainLoop(flag),
          timetvl(timetvl), connectionTimeout(timeout), isStop(false),
          wakeupFd(eventfd(0, EFD_NONBLOCK)), wakeChannel(new Channel(this, wakeupFd)),
          timerFd(CreateTimerFd(timeout)), timerChannel(new Channel(this, timerFd))
{
    wakeChannel->onRead = [this] { HandleWakeUp(); };
    wakeChannel->EnableReading();
    timerChannel->onRead = [this] { HandleTimer(); };
    timerChannel->EnableReading();
}

EventLoop::~EventLoop()
= default;

void EventLoop::Run()
{
    threadId = ::syscall(SYS_gettid);
    while (!isStop) {
        std::vector<Channel *> channels = ep->Wait(10000);
        if (channels.empty()) {
            onEpollTimeout(this);
            continue;
        }
        for (auto &ch: channels) {
            ch->HandleEvent();
        }
    }
}

void EventLoop::Stop()
{
    isStop = true;
    WakeUp();
}

void EventLoop::WakeUp() const
{
    uint64_t val = 1;
    ::write(wakeupFd, &val, sizeof(val));
}

void EventLoop::HandleWakeUp()
{
    uint64_t val;
    ::read(wakeupFd, &val, sizeof(val));
    std::lock_guard<std::mutex> gd(queueMutex);
    while (!taskQueue.empty()) {
        auto fn = std::move(taskQueue.front());
        taskQueue.pop();
        fn();
    }
}

void EventLoop::HandleTimer()
{
    struct itimerspec timeout{};
    memset(&timeout, 0, sizeof(struct itimerspec));
    timeout.it_value.tv_sec = timetvl;                             // 定时时间，固定为5，方便测试。
    timeout.it_value.tv_nsec = 0;
    timerfd_settime(timerFd, 0, &timeout, nullptr);
    if (mainLoop) {
        return;
    }
    time_t now = time(nullptr);
    for (auto it = conns.begin(); it != conns.end();) {
        if (it->second->Timeout(now, connectionTimeout)) {
            syslog(LOG_INFO, "EventLoop::HandleTimer() thread is %ld.\n", syscall(SYS_gettid));
            onTimer(it->first);
            std::lock_guard<std::mutex> gd(mapMutex);
            it = conns.erase(it);
        } else it++;
    }
}

void EventLoop::OnConnection(std::shared_ptr<Connection> &ptr)
{
    std::lock_guard<std::mutex> gd(mapMutex);
    conns[ptr->GetFd()] = ptr;
}

void EventLoop::UpdateChannel(Channel *ch)
{
    ep->UpdateChannel(ch);
}

void EventLoop::RemoveChannel(Channel *ch)
{
    ep->RemoveChannel(ch);
}

void EventLoop::QueueInLoop(const std::function<void()>& fn)
{
    {
        std::lock_guard<std::mutex> gd(queueMutex);
        taskQueue.push(fn);
    }
    WakeUp();
}

bool EventLoop::IsInLoopThread() const
{
    return threadId == ::syscall(SYS_gettid);
}